﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Common.Exceptions.UserExceptions;

namespace Simhopp.Common
{
    /// <summary>
    /// The Score class provides a score value and the judge who committed the score.
    /// </summary>
    [Serializable]
    public class Score : ISerializable, IDatabaseObject
    {
        #region FIELD REGION
        private int id;
        private int diveId;
        private User judge;
        private int judgeId;
        private double value;
        #endregion
        #region CONSTRUCTOR REGION
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="judge">The judge who committed the score.</param>
        /// <param name="value">The value of the score.</param>
        public Score(User judge = null, double value = 0.0)
        {
            id = -1;
            if(judge != null && !(judge.AccountType == AccountTypes.Judge || judge.AccountType == AccountTypes.HeadJudge))
                throw new WrongUserException("Only Judges and Head Judges can submit scores");
            this.judge = judge;
            if(judge != null)
                this.judgeId = judge.Id;
            this.value = value;
        }

        /// <summary>
        /// Empty default constructor - needed for the database
        /// </summary>
        public Score()
        {
            id = -1;
        }

        #endregion
        #region PROPERTY REGION
        /// <summary>
        /// Property Id gets or sets the id of the score.
        /// </summary>
        public int Id
        {
            get { return id; }
            set { this.id = value; }
        }
        /// <summary>
        /// Property Judge gets or sets the judge who committed the score.
        /// </summary>
        public User Judge
        {
            get { return judge; }
            set 
            { 
                judge = value;
                judgeId = judge.Id;
            }
        }
        /// <summary>
        /// Property Value gets or sets the value of the score.
        /// </summary>
        public double Value
        {
            get { return value; }
            set { this.value = value; }
        }
        /// <summary>
        /// Property JudgeId gets or sets the judges id of the score.
        /// </summary>
        public int JudgeId
        {
            get { return judgeId; }
            set { this.judgeId = value; }
        }
        /// <summary>
        /// Property DiveId gets or sets the diveId of the score.
        /// </summary>
        public int DiveId
        {
            get { return diveId; }
            set { this.diveId = value; }
        }
        #endregion
        #region METHOD REGION
        /// <summary>
        /// Checks if two instances of the object are identical
        /// </summary>
        /// <param name="obj">The object to compare with</param>
        /// <returns>True/False</returns>
        public override bool Equals(object obj)
        {
            return ((Score)obj).Id == this.Id;
        }

        /// <summary>
        /// Used in addition to Equals method
        /// </summary>
        /// <returns>A unique id</returns>
        public override int GetHashCode()
        {
            return id;
        }

        #endregion
        #region DATABASE REGION
        /// <summary>
        /// Creates a dictionary from the object where member field names are mapped to their corresponding values
        /// </summary>
        /// <returns>A dictionary</returns>
        public Dictionary<String, Object> GetDictionary()
        {
            try
            {
                var dictionary = new Dictionary<String, Object>();
                dictionary.Add("Id", this.Id.ToString());
                dictionary.Add("Value", this.Value.ToString().Replace(",", "."));
                if(this.judgeId != 0)
                    dictionary.Add("JudgeId", this.JudgeId.ToString());
                dictionary.Add("DiveId", this.DiveId.ToString());
                return dictionary;
            }
            catch (Exception e)
            {
                MyLogger.Error("Failed to create dictionary from Score object - " + e.Message);
                return null;
            }
        }

        /// <summary>
        /// Creates an Score object from a passed dictionary
        /// </summary>
        /// <param name="data">A dictionary where database field names are mapped to their corresponding values</param>
        /// <returns>A Score object</returns>
        public static Score GetFromDictionary(Dictionary<String, Object> data)
        {
            try
            {
                var score = new Score
                {
                    Id = (int)data["Id"],
                    Value = (double)data["Value"],
                    DiveId = (int)data["DiveId"],
                    JudgeId = (int)data["JudgeId"]
                };
                return score;
            }
            catch (Exception e)
            {
                MyLogger.Error("Failed to create User object from dictionary - " + e.Message);
                return null;
            }
        }

        /// <summary>
        /// Returns the name of the Id field used in the corresponding database-table
        /// </summary>
        /// <returns></returns>
        public String GetIdFieldName()
        {
            return "Id";
        }

        /// <summary>
        /// Gets the table name as a string.
        /// </summary>
        /// <returns>The table name as a string.</returns>
        public String GetTableName()
        {
            return "Scores";
        }
        #endregion
        #region SERIALIZE/DESERIALIZE REGION
        /// <summary>
        /// Adds the class specific information used in the serialization of an object.
        /// </summary>
        /// <param name="info">The class specific information.</param>
        /// <param name="context">Describes the source and destination of a given serialized stream.</param>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Judge", judge, typeof(User));
            info.AddValue("Value", value, typeof(double));
            info.AddValue("DiveId", diveId, typeof(int));
        }
        /// <summary>
        /// Constructor used in the deserialization of an object.
        /// </summary>
        /// <param name="info">The class specific information.</param>
        /// <param name="context">Describes the source and destination of a given serialized stream.</param>
        public Score(SerializationInfo info, StreamingContext context)
        {
            judge = (User)info.GetValue("Judge", typeof(User));
            value = (double)info.GetValue("Value", typeof(double));
            diveId = (int)info.GetValue("DiveId", typeof(int));
        }
        #endregion
    }
}
